package org.osflash.signals;

import asunit.asserts.*;
import asunit.framework.IAsync;
import flash.events.Event;

class ISignalTestBase {

	@:meta(Inject())
	public var async : IAsync;
	var signal : ISignal;
	@:meta(After())
	public function destroySignal() : Void {
		signal.removeAll();
		signal = null;
	}

	@:meta(Test())
	public function numListeners_is_0_after_creation() : Void {
		assertEquals(0, signal.numListeners);
	}

	@:meta(Test())
	public function addOnce_and_dispatch_should_remove_listener_automatically() : Void {
		signal.addOnce(newEmptyHandler());
		dispatchSignal();
		assertEquals("there should be no listeners", 0, signal.numListeners);
	}

	@:meta(Test())
	public function add_listener_then_remove_then_dispatch_should_not_call_listener() : Void {
		signal.add(failIfCalled);
		signal.remove(failIfCalled);
		dispatchSignal();
	}

	@:meta(Test())
	public function add_listener_then_remove_function_not_in_listeners_should_do_nothing() : Void {
		signal.add(newEmptyHandler());
		signal.remove(newEmptyHandler());
		assertEquals(1, signal.numListeners);
	}

	@:meta(Test())
	public function add_2_listeners_remove_2nd_then_dispatch_should_call_1st_not_2nd_listener() : Void {
		var called : Bool = false;
		signal.add(function(e : Dynamic = null) : Void {
			called = true;
		}
);
		signal.add(failIfCalled);
		signal.remove(failIfCalled);
		dispatchSignal();
		assertTrue(called);
	}

	@:meta(Test())
	public function add_2_listeners_should_yield_numListeners_of_2() : Void {
		signal.add(newEmptyHandler());
		signal.add(newEmptyHandler());
		assertEquals(2, signal.numListeners);
	}

	@:meta(Test())
	public function add_2_listeners_then_remove_1_should_yield_numListeners_of_1() : Void {
		var firstFunc : Dynamic = newEmptyHandler();
		signal.add(firstFunc);
		signal.add(newEmptyHandler());
		signal.remove(firstFunc);
		assertEquals(1, signal.numListeners);
	}

	@:meta(Test())
	public function add_2_listeners_then_removeAll_should_yield_numListeners_of_0() : Void {
		signal.add(newEmptyHandler());
		signal.add(newEmptyHandler());
		signal.removeAll();
		assertEquals(0, signal.numListeners);
	}

	@:meta(Test())
	public function add_same_listener_twice_should_only_add_it_once() : Void {
		var func : Dynamic = newEmptyHandler();
		signal.add(func);
		signal.add(func);
		assertEquals(1, signal.numListeners);
	}

	@:meta(Test())
	public function addOnce_same_listener_twice_should_only_add_it_once() : Void {
		var func : Dynamic = newEmptyHandler();
		signal.addOnce(func);
		signal.addOnce(func);
		assertEquals(1, signal.numListeners);
	}

	@:meta(Test())
	public function add_two_listeners_and_dispatch_should_call_both() : Void {
		var calledA : Bool = false;
		var calledB : Bool = false;
		signal.add(function(e : Dynamic = null) : Void {
			calledA = true;
		}
);
		signal.add(function(e : Dynamic = null) : Void {
			calledB = true;
		}
);
		dispatchSignal();
		assertTrue(calledA);
		assertTrue(calledB);
	}

	@:meta(Test())
	public function add_the_same_listener_twice_should_not_throw_error() : Void {
		var listener : Dynamic = newEmptyHandler();
		signal.add(listener);
		signal.add(listener);
	}

	@:meta(Test())
	public function dispatch_2_listeners_1st_listener_removes_itself_then_2nd_listener_is_still_called() : Void {
		signal.add(selfRemover);
		// async.add verifies the second listener is called
		signal.add(async.add(newEmptyHandler(), 10));
		dispatchSignal();
	}

	function selfRemover(e : Dynamic = null) : Void {
		signal.remove(selfRemover);
	}

	@:meta(Test())
	public function dispatch_2_listeners_1st_listener_removes_all_then_2nd_listener_is_still_called() : Void {
		signal.add(async.add(allRemover, 10));
		signal.add(async.add(newEmptyHandler(), 10));
		dispatchSignal();
	}

	function allRemover(e : Dynamic = null) : Void {
		signal.removeAll();
	}

	@:meta(Test())
	public function adding_a_listener_during_dispatch_should_not_call_it() : Void {
		signal.add(async.add(addListenerDuringDispatch, 10));
		dispatchSignal();
	}

	function addListenerDuringDispatch(e : Dynamic = null) : Void {
		signal.add(failIfCalled);
	}

	//TODO: clarify test purpose through naming and/or implementation
	@:meta(Test())
	public function can_use_anonymous_listeners() : Void {
		var slots : Array<Dynamic> = [];
		var i : Int = 0;
		while(i < 10) {
			slots.push(signal.add(newEmptyHandler()));
			i++;
		}
		assertEquals("there should be 10 listeners", 10, signal.numListeners);
		for(var slot : ISlot in slots) {
			signal.remove(slot.listener);
		}

		assertEquals("all anonymous listeners removed", 0, signal.numListeners);
	}

	//TODO: clarify test purpose through naming and/or implementation
	@:meta(Test())
	public function can_use_anonymous_listeners_in_addOnce() : Void {
		var slots : Array<Dynamic> = [];
		var i : Int = 0;
		while(i < 10) {
			slots.push(signal.addOnce(newEmptyHandler()));
			i++;
		}
		assertEquals("there should be 10 listeners", 10, signal.numListeners);
		for(var slot : ISlot in slots) {
			signal.remove(slot.listener);
		}

		assertEquals("all anonymous listeners removed", 0, signal.numListeners);
	}

	@:meta(Test())
	public function add_listener_returns_slot_with_same_listener() : Void {
		var listener : Dynamic = newEmptyHandler();
		var slot : ISlot = signal.add(listener);
		assertSame(listener, slot.listener);
	}

	@:meta(Test())
	public function remove_listener_returns_same_slot_as_when_it_was_added() : Void {
		var listener : Dynamic = newEmptyHandler();
		var slot : ISlot = signal.add(listener);
		assertSame(slot, signal.remove(listener));
	}

	function dispatchSignal() : Void {
		signal.dispatch(new Event("test"));
	}

	////// UTILITY METHODS //////
	static function newEmptyHandler() : Dynamic {
		return function(e : Dynamic = null) : Void {
		}
;
	}

	static function failIfCalled(e : Dynamic = null) : Void {
		fail("This function should not have been called.");
	}

}

